package top.rhynie.web3j;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.bouncycastle.util.encoders.Hex;
import org.tron.tronj.client.TronClient;
import org.tron.tronj.client.exceptions.IllegalException;
import org.tron.tronj.crypto.SECP256K1;
import org.tron.tronj.proto.Chain;
import org.tron.tronj.proto.Response;
import org.tron.tronj.utils.Base58Check;
import top.rhynie.web3j.utils.GsonUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Web3J {

  private TronClient client;

  public TronClient getClient() {
    return this.client;
  }

  public Web3J(TronClient client) {
    this.client = client;
  }

  /**
   * trx转账
   * @param from 发送者地址
   * @param to  接收者地址
   * @param amount  余额
   * @return
   */
  public boolean transferTrx(String from, String to, long amount) {
    try {
      Response.TransactionExtention transaction = client.transfer(from, to, amount);
      Chain.Transaction signedTxn = client.signTransaction(transaction);
      Response.TransactionReturn ret = client.broadcastTransaction(signedTxn);
      return ret.getResult();
    } catch (IllegalException e) {
      e.printStackTrace();
    }
    return false;
  }

  /**
   * 生成账户
   * @return
   */
  public List<String> generateAddress() {
    SECP256K1.KeyPair kp = SECP256K1.KeyPair.generate();
    SECP256K1.PublicKey pubKey = kp.getPublicKey();
    Keccak.Digest256 digest = new Keccak.Digest256();
    digest.update(pubKey.getEncoded(), 0, 64);
    byte[] raw = digest.digest();
    byte[] rawAddr = new byte[21];
    rawAddr[0] = 65;
    System.arraycopy(raw, 12, rawAddr, 1, 20);
    String account = Base58Check.bytesToBase58(TronClient.parseHex(Hex.toHexString(rawAddr)).toByteArray());
    String privateKey = Hex.toHexString(kp.getPrivateKey().getEncoded());
    List<String> accountInfo = new ArrayList<>();
    accountInfo.add(account);
    accountInfo.add(privateKey);
    return accountInfo;
  }

  /**
   * 创建账户并激活
   * @param creator 创建者地址
   * @return
   */
  public Map<String, Object> createAccount(String creator) {
    Map<String, Object> map = new HashMap<>();
    List<String> accountInfo = generateAddress();
    String account = accountInfo.get(0);
    String privateKey = accountInfo.get(1);
    map.put("account", account);
    map.put("privateKey", privateKey);
    Response.TransactionReturn ret = null;
    try {
      Response.TransactionExtention transaction = client.createAccount(creator, account);
      Chain.Transaction signedTxn = client.signTransaction(transaction);
      ret = client.broadcastTransaction(signedTxn);
      map.put("active", ret.getResult());
      return map;
    } catch (IllegalException e) {
      e.printStackTrace();
    }
    map.put("active", false);
    return map;
  }

  public void generateContractClass(String abi, String className, String packagePath, String packageName) {
    packagePath = (packagePath + packageName).replace(".", "\\");
    File file = new File(packagePath);
    if(!file.exists()) {
      file.mkdirs();
    }
    List<Map<String, Object>> maps = GsonUtil.GsonToListMaps(abi);
    maps.stream()
        .forEach(m -> {
          m.keySet().forEach(k -> System.out.println(k + "----" + m.get(k)));
          System.out.println("--------------------------------------");
        });
    Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
    OutputStream fos = null;
    try {
      String filePath = this.getClass().getClassLoader().getResource("template").getFile();
      cfg.setDirectoryForTemplateLoading(new File(filePath));
      cfg.setDefaultEncoding("UTF-8");
      cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);

      Template template = cfg.getTemplate("contract.ftl");

      Map<String, Object> dataModel = new HashMap<>();
      dataModel.put("packageName", packageName);
      dataModel.put("className", className);

      List<Map<String, Object>> functions = maps.stream().filter(m -> "function".equals(m.get("type")))
          .collect(Collectors.toList());
      dataModel.put("functions", functions);

      File dir = new File(packagePath);

      fos = new FileOutputStream( new File(dir, className + ".java"));
      Writer out = new OutputStreamWriter(fos);

      template.process(dataModel, out);
      fos.flush();
    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      if(fos != null) {
        try {
          fos.close();
        } catch (IOException e) {
          e.printStackTrace();
        }
        ;
      }
    }
  }

}
